﻿using PlutoStudio.Net.Messages;
using PlutoStudio.Net.Processors.Security;
using PlutoStudio.Net.Tcp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace PlutoStudio.Net.Processors
{
    public abstract class NetContext
    {
        private System.Collections.Concurrent.ConcurrentDictionary<UInt16, InvokeAsyncResult> waiters = new System.Collections.Concurrent.ConcurrentDictionary<ushort, InvokeAsyncResult>();

        private UInt16 mId = 0;
        private object mIdLock = new object();

        public INetClient Client { get; private set; }

        public IPrincipal User { get; set; }

        public bool Closed { get; private set; }

        public bool ResponseError { get; set; }

        protected abstract string ProcessorConfigName { get; }

        private MessageProcessorFactory factory;

        protected NetContext()
        {
            ResponseError = true;
        }

        protected NetContext(INetClient client)
        {
            this.User = new GenericPrincipal(new GenericIdentity(), null);
            Closed = false;
            this.Client = client;
            client.Received += (o, e) =>
            {
                try
                {
                    TraceHelper.Helper.TraceInformation("收到消息：{0}", e.Message);
                    InvokeAsyncResult ar;
                    if (e.Message.StatusCode != 0 && waiters.TryRemove(e.Message.MessageId, out ar))
                    {
                        ar.Complate(e.Message);
                    }
                    else
                    {
                        TraceHelper.Helper.TraceInformation(string.Format("创建消息处理器:Flag:{0}", e.Message.Flag));
                        var processor = (factory ?? (factory = PlutoStudio.Net.Processors.MessageProcessorFactory.Factory())).CreateProcessor(ProcessorConfigName, e.Message);
                        TraceHelper.Helper.TraceInformation(string.Format("已创建消息处理器:Flag:{0}:type:{1}", e.Message.Flag, processor.GetType().FullName));
                        processor.Process(this, e);
                    }
                }
                catch (TypeLoadException ex)
                {
                    TraceHelper.Helper.TraceWaring("加载消息处理器发生异常。 \r\n   {0}", ex.ToString());
                    OnLoadProcessFaild(e.Message, ex);
                }
            };
            client.Closed += (o, e) =>
            {
                Close();
            };
        }

        public void BeginProcess()
        {
            Client.BeginReceive();
        }

        protected virtual void OnLoadProcessFaild(Message message, TypeLoadException ex)
        {

        }

        public void Send(Message message)
        {
            Client.Send(message);
        }

        public IAsyncResult BeginInvoke(Message message, AsyncCallback callback, object state)
        {
            message.MessageId = GetMessageId();
            var ar = new InvokeAsyncResult(callback, state);
            waiters.TryAdd(message.MessageId, ar);
            Send(message);
            return ar;
        }

        public Message EndInvoke(IAsyncResult ar)
        {
            var iar = ar as InvokeAsyncResult;
            iar.Wait();
            return iar.Message;
        }

        public Task<Message> InvokeAsync(Message message)
        {
            return Task<Message>.Factory.FromAsync<Message>(BeginInvoke, EndInvoke, message, null);
        }

        public T EndInvoke<T>(IAsyncResult ar, out Message message)
        {
            var msg = EndInvoke(ar);
            var content = Encoding.UTF8.GetString(msg.Content);
            message = msg;
            return content.Deserialize<T>();
        }

        public T EndInvoke<T>(IAsyncResult ar)
        {
            var msg = EndInvoke(ar);
            var content = Encoding.UTF8.GetString(msg.Content);
            return content.Deserialize<T>();
        }


        private UInt16 GetMessageId()
        {
            lock (mIdLock)
            {
                if (mId >= UInt16.MaxValue)
                {
                    mId = 1;
                }
                else
                {
                    mId++;
                }
                return mId;
            }
        }

        public virtual void Close()
        {
            if (!Closed)
            {
                Closed = true;
                Client.Close();
            }
        }

    }

    internal class InvokeAsyncResult : IAsyncResult
    {
        private System.Threading.ManualResetEventSlim waitHandle;
        private AsyncCallback userCallback;
        public object AsyncState { get; private set; }
        public Message Message { get; private set; }
        private bool isCompleted = false;

        public InvokeAsyncResult(AsyncCallback callback, object state)
        {
            this.userCallback = callback;
            this.AsyncState = state;
            waitHandle = new System.Threading.ManualResetEventSlim();
        }

        public void Wait()
        {
            try
            {
                this.waitHandle.Wait();
            }
            finally
            {
                this.waitHandle.Dispose();
            }
        }

        public void Complate(Message message)
        {
            this.Message = message;
            this.isCompleted = true;
            this.waitHandle.Set();
            if (this.userCallback != null)
            {
                this.userCallback(this);
            }
        }

        public System.Threading.WaitHandle AsyncWaitHandle
        {
            get
            {
                return waitHandle.WaitHandle;
            }
        }

        public bool CompletedSynchronously
        {
            get { return false; }
        }

        public bool IsCompleted
        {
            get { return isCompleted; }
        }
    }

}
